﻿using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.IO;
using System.Linq;
using Futaba.SBus.Extensions;
using System.Collections.Generic;
using Futaba.SBus.Readers;
using Futaba.SBus.Telemetry;
using Futaba.SBus.Telemetry.Configuration;
using System.Diagnostics;

namespace Futaba.SBus.Tests.Telemetry
{
	[TestClass]
	public class TelemetryParserTests : BaseTest
	{

		[TestMethod, TestCategory("SBUS2")]
		[DeploymentItem(@"Samples\TxData\DistanceAbove255.log")]
		[DeploymentItem(@"Samples\slots.xml")]
		public void ReadPacket1()
		{
			var reader = new FileSbusStreamReader("DistanceAbove255.log", packetsPerSecond: 0);
			reader.OpenPort();
			var slots = LoadSlots();
			var target = new TelemetryParser();
			target.TxTelemetryPassthroughMode = true; //must be set to process sequence bytes
			target.ItemsCollection = slots;
			target.Reader = reader;

			reader.Seek(24, SeekOrigin.Begin);
			var actual = target.ReadPacket();
			PrintSlots(target.ItemsCollection);
			TestContext.WriteLine("Bytes ignored: {0}", target.BytesSkipped);
			TestContext.WriteLine("File-Offset: {0}", reader.FileOffset);
			reader.ReleasePort();
			Assert.IsTrue(actual);
			Assert.AreEqual(1, target.PacketsReceived);
			Assert.AreEqual(0, target.BytesSkipped);
		}

		[TestMethod, TestCategory("SBUS2"), TestCategory("SBUS2.RPM")]
		[DeploymentItem(@"Samples\TxData\DistanceAbove255.log")]
		[DeploymentItem(@"Samples\slots.xml")]
		public void ReadPacket_RpmNotNegative()
		{
			var reader = new FileSbusStreamReader("DistanceAbove255.log", packetsPerSecond: 0);
			reader.OpenPort();
			var slots = LoadSlots();
			var target = new TelemetryParser();
			target.TxTelemetryPassthroughMode = true; //must be set to process sequence bytes
			target.ItemsCollection = slots;
			target.Reader = reader;

			reader.Seek(73, SeekOrigin.Begin);
			var actual = target.ReadPacket();
			PrintSlots(target.ItemsCollection);
			TestContext.WriteLine("Bytes ignored: {0}", target.BytesSkipped);
			reader.ReleasePort();
			Assert.IsTrue(actual);
			Assert.AreEqual(1, target.PacketsReceived);
			Assert.AreEqual(0, target.BytesSkipped);
			Assert.IsTrue(target.ItemsCollection.Single(s => s.SensorType == SensorType.RpmSensor).CurrentValue > 0);
		}

		[TestMethod, TestCategory("SBUS2"), TestCategory("SBUS2.RPM")]
		[DeploymentItem(@"Samples\TxData\DistanceAbove255.log")]
		[DeploymentItem(@"Samples\slots.xml")]
		public void ReadPacket_Multiple_Rpm()
		{
			var reader = new FileSbusStreamReader("DistanceAbove255.log", packetsPerSecond: 0);
			reader.OpenPort();
			var slots = LoadSlots();
			var target = new TelemetryParser();
			target.TxTelemetryPassthroughMode = true; //must be set to process sequence bytes
			target.ItemsCollection = slots;
			target.Reader = reader;

			var skipPackets = 0;
			var channelPacketByteCount = 24;
			var seekPosition = channelPacketByteCount + (skipPackets * 49);

			var rpmSlot = target.ItemsCollection.Single(s => s.SensorType == SensorType.RpmSensor);
			var packetsReceived = 0;

			while (!reader.EndOfStream && packetsReceived < 500)
			{
				target.BytesSkipped = 0;
				reader.Seek(channelPacketByteCount, SeekOrigin.Current);
				var actual = target.ReadPacket();
				if (actual)
					packetsReceived++;
				TestContext.WriteLine("{0}", rpmSlot);
				if (target.BytesSkipped > 0)
					TestContext.WriteLine("Bytes ignored: {0}", target.BytesSkipped);
			}
			//Assert.IsTrue(actual);
			Assert.AreEqual(packetsReceived, target.PacketsReceived);
			reader.ReleasePort();
		}

		[TestMethod, TestCategory("SBUS2"), TestCategory("SBUS2.GPS")]
		[DeploymentItem(@"Samples\TxData\DistanceAbove255.log")]
		[DeploymentItem(@"Samples\slots.xml")]
		public void ReadPacket_Multiple_Distance()
		{
			var reader = new FileSbusStreamReader("DistanceAbove255.log", packetsPerSecond: 0);
			reader.OpenPort();
			var slots = LoadSlots();
			var target = new TelemetryParser();
			target.TxTelemetryPassthroughMode = true; //must be set to process sequence bytes
			target.ItemsCollection = slots;
			target.Reader = reader;

			var skipPackets = 0;
			var channelPacketByteCount = 24;
			var seekPosition = channelPacketByteCount + (skipPackets * 49);

			var distanceSlot = target.ItemsCollection.Single(s => s.SensorType == SensorType.GpsSensor && s.Parameter.ValueType == SensorValueType.Distance);
			var packetsReceived = 0;

			while (!reader.EndOfStream && packetsReceived < 500)
			{
				target.BytesSkipped = 0;
				reader.Seek(channelPacketByteCount, SeekOrigin.Current);
				var actual = target.ReadPacket();
				if (actual)
					packetsReceived++;
				TestContext.WriteLine("{0}", distanceSlot);
				if (target.BytesSkipped > 0)
					TestContext.WriteLine("Bytes ignored: {0}", target.BytesSkipped);
			}
			//Assert.IsTrue(actual);
			Assert.AreEqual(packetsReceived, target.PacketsReceived);
			reader.ReleasePort();
		}

		[TestMethod, TestCategory("SBUS2"), TestCategory("SBUS2.GPS")]
		[DeploymentItem(@"Samples\TxData\DistanceAbove255.log")]
		[DeploymentItem(@"Samples\slots.xml")]
		public void ReadPacket_Gps()
		{
			var reader = new FileSbusStreamReader("DistanceAbove255.log", packetsPerSecond: 0);
			reader.OpenPort();
			var slots = LoadSlots();
			var target = new TelemetryParser();
			target.TxTelemetryPassthroughMode = true; //must be set to process sequence bytes
			target.ItemsCollection = slots;
			target.Reader = reader;

			/* SpeedAbove255.log @123
			 * 1b 40 17 Speed = 23
			 * 9b 40 28 Altitude = 40
			 * 5b 01 0c Distance = 268
			 * db ff f9 Vario = -0.7
			 * 3b 30 03 bb c2 5c Lat
			 * 7b 10 01 fb b5 79 Lon
			 * */

			reader.Seek(122, SeekOrigin.Begin);
			var actual = target.ReadPacket();
			reader.ReleasePort();

			Assert.IsTrue(actual, "packet not parsed");
			PrintSlots(slots);
			if (target.BytesSkipped > 0)
				TestContext.WriteLine("Bytes ignored: {0}", target.BytesSkipped);
			Assert.AreEqual(1, target.PacketsReceived);

			var gpsSlots = slots.Where(s => s.SensorType == SensorType.GpsSensor).OrderBy(s => s.Parameter.Position).ToArray();
			Assert.AreEqual(23, gpsSlots[0].CurrentValue);
			Assert.AreEqual(40, gpsSlots[1].CurrentValue);
			Assert.AreEqual(268, gpsSlots[2].CurrentValue);
			Assert.AreEqual(-0.7, Math.Round(gpsSlots[3].CurrentValue.GetValueOrDefault(),2));
		}

		[TestMethod, TestCategory("SBUS2")]
		[DeploymentItem(@"Samples\TxData\DistanceAbove255.log")]
		[DeploymentItem(@"Samples\slots.xml")]
		public void ReadPacket_Multiple_All()
		{
			var reader = new FileSbusStreamReader("DistanceAbove255.log", packetsPerSecond: 0);
			reader.OpenPort();
			var slots = LoadSlots();
			var target = new TelemetryParser();
			target.TxTelemetryPassthroughMode = true; //must be set to process sequence bytes
			target.ItemsCollection = slots;
			target.Reader = reader;

			var channelPacketByteCount = 24;

			var packetsReceived = 0;

			while (!reader.EndOfStream && packetsReceived < 500)
			{
				target.BytesSkipped = 0;
				reader.Seek(channelPacketByteCount, SeekOrigin.Current);
				Debug.WriteLine("FileOffset: {0}", reader.FileOffset);
				var actual = target.ReadPacket();
				if (actual)
					packetsReceived++;
				//PrintSlots(target.ItemsCollection);
				if (target.BytesSkipped > 0)
					TestContext.WriteLine("Bytes ignored: {0}", target.BytesSkipped);
			}
			TestContext.WriteLine("FileOffset: {0}", reader.FileOffset);
			TestContext.WriteLine("PacketsReceived: {0}", target.PacketsReceived);
			Assert.AreEqual(packetsReceived, target.PacketsReceived);
			reader.ReleasePort();
		}

		private void PrintSlots(IList<ISlot> list)
		{
			var info = list.Select(s => string.Format("{0} {1} {2}", s.SlotId, s.CurrentValue, s.Parameter.ValueLabel));
			TestContext.WriteLine("{0}", string.Join(", ", info));
		}

		private List<ISlot> LoadSlots()
		{
			using (var stream = File.OpenRead("slots.xml"))
			{
				var slots = SlotFactory.DefaultInstance.Load(stream).Cast<ISlot>().ToList();
				return slots;
			}
		}

	}
}
